LINQ inleiding
Bouwstenen van LINQ
De twee fundamentele bouwstenen van LINQ zijn elementen en reeksen.
Een reeks kan van worden gezien als een lijst van items, en elk item in de lijst als een element. Een reeks is een instantie van een klasse die de IEnumerable <T>
interface implementeert.
Nemen we een reeks van nummers die we als een array declaren int[] leeftijden = {50, 1, 11, 2, 28, 2, 13, 25, 50}
. de variabele leeeftijden
stelt een reeks voor waarvan elke int
in de array een individueel element is.
Een reeks kan een lokale reeks van objecten in het geheugen zijn of een externe reeks, zoals een SQL Server-database. Die externe gegevensbronnen (bijvoorbeeld SQL Server), deze externe reeksen worden ook door een IQueryable <T>
interface voorgesteld.
Queries, of meer specifiek query operatoren, zijn van toepassing op een invoerreeks en produceren één of meerdere output waarden. Deze outputwaarde kan een getransformeerde versie van de inputsequentie zijn, namelijk een uitvoer sequentie of enkele scalaire waarde zoals een telling van het aantal elementen in de ingevoerde sequentie.
Query's die worden uitgevoerd op de lokale sequenties staan bekend als lokale queries of LINQ-to-objects queries.
Er zijn een hele reeks van query-operatoren die geïmplementeerd worden als extensie-methoden in de statische System.Linq.Enumerable
klasse. Deze set van query's zijn de standaard query.
Een belangrijk ding om op te merken bij het gebruik van query's is dat ze de input reeks niet veranderen. In plaats van de input reeks te veranderen stuurt de query operator een nieuwe reeks (of scalaire waarde) terug.
Scalaire retourwaarden en outputreeksen
Een query operator retoruneert een outputreeks of een scalaire waarde. In de volgende code wordt de ingevoerde sequentie leeftijden
bewerkt, eerst door de Count
queryoperator, dat een enkele scalaire waarde retourneert, namelijk het aantal elementen in de ingevoerde sequentie. Vervolgens wordt dezelfde inputreeks bewerkt met de Distinct
queryoperator, die een nieuwe uitvoersequentie met de elementen uit de ingevoerde sequentie produceert, maar met de dubbele elementen eruit verwijderd.
int[] leeftijden = { 50, 1, 11, 2, 28, 2, 13, 25, 50 }; // scalar return value int numberOfElements = leeftijden.Count(); Console.WriteLine("Aantal leeftijden {0}", numberOfElements); // Output sequence return value IEnumerable < int > verschillendeLeeftijden = leeftijden.Distinct(); Console.WriteLine("Verschillende leeftijden in de outputreeks: "); foreach (int leeftijd in leeftijden) { Console.WriteLine(leeftijd); }
Als we de code uitvoeren krijgen we het volgende:
Uitgestelde uitvoering
De meeste queries worden niet onmiddellijk uitgevoerd. De uitvoering ervan wordt uitgesteld tot een later tijdstip in de uitvoering van het programma. Dit betekent dat de zoekopdracht niet wordt uitgevoerd wanneer ze wordt gemaakt, maar wanneer ze wordt gebruikt, bijvoorbeeld wanneer ze opgesomd wordt.
Uitgestelde uitvoering betekent dat de ingevoerde sequentie kan worden gewijzigd nadat de query is geconstrueerd, maar wel voordat de query wordt uitgevoerd. In de volgende code wordt de ingevoerde sequentie gewijzigd nadat de query is geconstrueerd, maar voordat deze wordt uitgevoerd.
int[] leeftijden = { 50, 1, 11, 2, 28, 2, 13, 25, 50 }; // scalar return value // construct the query IEnumerable<int> ouderDan11 = leeftijden.Where(x => x > 10); // Change the third element leeftijden[2] = 100; // At this point the query has been created but not executed Console.WriteLine("Ouder dan 11: "); foreach (int leeftijd in ouderDan11) { Console.WriteLine(leeftijd); }
De query wordt niet uitgevoerd totdat de foreach
wordt uitgevoerd. De resultaten van het uitvoeren van deze code zijn:
Merk op dat de waarde 100 is opgenomen in de resultaten, niettegenstaande het derde element op het moment dat de query werd gemaakt, nog steeds de waarde van 11 had.
De queries die een een scalaire waarde retourneren (of een enkel element van de ingevoerde sequentie) zoals Count
, Min
en Last
werken niet op deze uitgestelde executie manier. Dus bij het gebruik van operatoren zoals Count
zal de queryonmiddellijk worden uitgevoerd, en niet uitgesteld.
Een aantal van de conversie operatoren worden ook onmiddellijk uitgevoerd, zoals ToList
, ToArray
, ToLookup
en ToDictionary
.
In de volgende code wordt de leeftijden matrix worden gewijzigd net zoals in het voorgaande voorbeeld. Maar in dit voorbeeld werd de query op het moment dat de wijziging plaatsvindt reeds uitgevoerd vanwege de extra ToArray()
operator. De wijziging zal niet worden opgenomen in de foreach
.
int[] leeftijden = { 50, 1, 11, 2, 28, 2, 13, 25, 50 }; // scalar return value // construct the query IEnumerable<int> ouderDan11 = leeftijden.Where(x => x > 10).ToArray(); // Change the third element leeftijden[2] = 100; // At this point the query has been created and // executed because of the ToArray() operator Console.WriteLine("Ouder dan 11: "); foreach (int leeftijd in ouderDan11) { Console.WriteLine(leeftijd); }
De waarde 100 is hier niet meer aanwezig, omdat de inputreeks werd gewijzigd nadat de query is uitgevoerd door de ToArray()
operator.
Lambda expressies in query operatoren
Sommige query's accepteren anonieme functies als argument waarmee je aangepaste logica aan de query kan doorgeven. Deze aangepaste logica kan je aan de query doorgeven door middel van een lambda-expressie.
De leeftijden.Where (x => x > 11)
code in het voorgaande codevoorbeeld is een voorbeeld van een query operator waaraan een anonieme functie wordt doorgegeven. De lambda-expressie x => x > 11
zorgt ervoor dat alleen de elementen (integers in dit geval) die groter zijn dan 11 gertourneerd worden.
Wanneer een queryoperator een lambda-expressie als argument meekrijgt, wordt de logica van de lambda-expressie op elk element van de invoer-sequentie toegepast.
Het type lambda expressie, die wordt ingevoerd in een queryoperator, is afhankelijk van de taak die de queryoperator uitvoert. In de volgende figuur zien we de handtekening van de Where-query operator. Het inputelement int wordt voorzien en een bool
moet worden teruggestuurd die bepaalt of het element wordt opgenomen in de uitvoer-sequentie.
In plaats van een lambda-expressie kan je een klassieke delegate, die verwijst naar een methode, gebruiken.
Geïnterpreteerde (interpreted) queries
Tot nu toe hebben we ons gericht op lokale queries. Lokale queries manipuleren IEnumerable<T>
sequenties. Lokale queries zijn het resultaat van de uitgevoerde query's, die zijn gedefinieerd in de System.Linq.Enumerable
klasse. Op die manier resulteert het uitvoeren van een lokale query in code die wordt gedefinieerd tijdens het compileren.
Geïnterpreteerde queries, aan de andere kant, beschrijven slechts de "vorm" van de query die wordt geïnterpreteerd tijdens runtime - vandaar de naam "geïnterpreteerd." Geïnterpreteerde queries manipuleren IQueryable<T>
sequenties, en de LINQ operatoren worden omgezet naar methoden die gedefinieerd zijn in de System.Linq.Queryable
klasse in plaats van de System.Linq.Enumerable
klasse.
De lokale (IEnumerable<T>) queries bevatten de werkelijke implementatie van de query code die wordt uitgevoerd, terwijl de geïnterpreteerde queries (IQueryable <T>) niet. Bij geïnterpreteerde queries, is de werkelijke query code, die wordt uitgevoerd, gedefinieerd in een query provider. De query provider ontvangt de beschrijving van de query die moet worden uitgevoerd en voert de query vervolgens uit (bijvoorbeeld het uitvoeren van SQL op een database) en retourneert tenslotte het resultaat.